/*
就像 Generator 函数返回一个同步遍历器对象一样， 异步 Generator 函数的作用， 是返回一个异步遍历器对象。

在语法上， 异步 Generator 函数就是async函数与 Generator 函数的结合。
*/

async function* gen() {
	yield 'hello';
}

const genObj = gen();

genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }

/*
上面代码中，gen是一个异步 Generator 函数，
执行后返回一个异步 Iterator 对象。
对该对象调用next方法，返回一个 Promise 对象。
*/



/*
异步遍历器的设计目的之一，就是 Generator 函数处理同步操作和异步操作时，能够使用同一套接口。
*/

// 同步 Generator 函数    这个好屌
function* map(iterable, func) {
	const iter = iterable[Symbol.iterator]();
	while (true) {
		const {
			value,
			done
		} = iter.next();
		if (done) break;
		yield func(value);
	}
}

// 异步 Generator 函数
async function* map(iterable, func) {
	const iter = iterable[Symbol.asyncIterator]();
	while (true) {
		const {
			value,
			done
		} = await iter.next();
		if (done) break;
		yield func(value);
	}
}

/*
上面代码中，map是一个 Generator 函数，第一个参数是可遍历对象iterable，第二个参数是一个回调函数func。
map的作用是将iterable每一步返回的值，使用func进行处理。
上面有两个版本的map，前一个处理同步遍历器，后一个处理异步遍历器，可以看到两个版本的写法基本上是一致的。
*/



/*
下面是另一个异步 Generator 函数的例子。
*/

async function* readLines(path) {
	let file = await fileOpen(path);

	try {
		while (!file.EOF) {
			yield await file.readLine();
		}
	} finally {
		await file.close();
	}
}

/*
上面代码中， 异步操作前面使用await关键字标明， 即await后面的操作， 应该返回 Promise 对象。 
凡是使用yield关键字的地方， 就是next方法停下来的地方， 
它后面的表达式的值（ 即await file.readLine() 的值），
 会作为next() 返回对象的value属性， 这一点是与同步 Generator 函数一致的。
 */


/*
异步 Generator 函数内部， 能够同时使用await和yield命令。 
可以这样理解， await命令用于将外部操作产生的值输入函数内部， yield命令用于将函数内部的值输出。
*/

//上面代码定义的异步 Generator 函数的用法如下。

(async function() {
	for await (const line of readLines(filePath)) {
		console.log(line);
	}
})()


//异步 Generator 函数可以与for await...of循环结合起来使用。

async function* prefixLines(asyncIterable) {
	for await (const line of asyncIterable) {
		yield '> ' + line;
	}
}


/*
异步 Generator 函数的返回值是一个异步 Iterator， 即每次调用它的next方法， 会返回一个 Promise 对象，
 也就是说， 跟在yield命令后面的， 应该是一个 Promise 对象。
  如果像上面那个例子那样， yield命令后面是一个字符串， 会被自动包装成一个 Promise 对象。
  
  异步内指令大部分后面跟promise包装成的对象
  */

function fetchRandom() {
	const url = 'https://www.random.org/decimal-fractions/' +
		'?num=1&dec=10&col=1&format=plain&rnd=new';
	return fetch(url);
}

async function* asyncGenerator() {
	console.log('Start');
	const result = await fetchRandom(); // (A)
	yield 'Result: ' + await result.text(); // (B)
	console.log('Done');
}

const ag = asyncGenerator();
ag.next().then(({
	value,
	done
}) => {
	console.log(value);
})

/*
上面代码中，ag是asyncGenerator函数返回的异步遍历器对象。
调用ag.next()以后，上面代码的执行顺序如下。
*/

ag.next() 立刻返回一个 Promise 对象。
asyncGenerator函数开始执行， 打印出Start。
await命令返回一个 Promise 对象， asyncGenerator函数停在这里。
A 处变成 fulfilled 状态， 产生的值放入result变量， asyncGenerator函数继续往下执行。
函数在 B 处的yield暂停执行， 一旦yield命令取到值， ag.next() 返回的那个 Promise 对象变成 fulfilled 状态。
ag.next() 后面的then方法指定的回调函数开始执行。 该回调函数的参数是一个对象 {
	value,
	done
}，
其中value的值是yield命令后面的那个表达式的值， done的值是false。



//A 和 B 两行的作用类似于下面的代码。

return new Promise((resolve, reject) => {
	fetchRandom()
		.then(result => result.text())
		.then(result => {
			resolve({
				value: 'Result: ' + result,
				done: false,
			});
		});
});


//如果异步 Generator 函数抛出错误，会导致 Promise 对象的状态变为reject，然后抛出的错误被catch方法捕获。

async function* asyncGenerator() {
	throw new Error('Problem!');
}

asyncGenerator()
	.next()
	.catch(err => console.log(err)); // Error: Problem!


/*
注意， 普通的 async 函数返回的是一个 Promise 对象， 而异步 Generator 函数返回的是一个异步 Iterator 对象。 
可以这样理解， async 函数和异步 Generator 函数， 是封装异步操作的两种方法， 都用来达到同一种目的。 
区别在于， 前者自带执行器， 后者通过for await ...	of执行， 或者自己编写执行器。 
*/

//下面就是一个异步 Generator 函数的执行器。
async function takeAsync(asyncIterable, count = Infinity) {
	const result = [];
	const iterator = asyncIterable[Symbol.asyncIterator]();
	while (result.length < count) {
		const {
			value,
			done
		} = await iterator.next();
		if (done) break;
		result.push(value);
	}
	return result;
}
/*
上面代码中，异步 Generator 函数产生的异步遍历器，会通过while循环自动执行，每当await iterator.next()完成，
就会进入下一轮循环。
一旦done属性变为true，就会跳出循环，异步遍历器执行结束。
*/

//下面是这个自动执行器的一个使用实例。

async function f() {
	async function* gen() {
		yield 'a';
		yield 'b';
		yield 'c';
	}

	return await takeAsync(gen());
}

f().then(function(result) {
	console.log(result); // ['a', 'b', 'c']
})

/*
异步 Generator 函数出现以后，JavaScript 就有了四种函数形式：
普通函数、async 函数、Generator 函数和异步 Generator 函数。

请注意区分每种函数的不同之处。
基本上，如果是一系列按照顺序执行的异步操作（比如读取文件，然后写入新内容，再存入硬盘），可以使用 async 函数；
如果是一系列产生相同数据结构的异步操作（比如一行一行读取文件），可以使用异步 Generator 函数。
*/

//异步 Generator 函数也可以通过next方法的参数， 接收外部传入的数据。

const writer = openFile('someFile.txt');
writer.next('hello'); // 立即执行
writer.next('world'); // 立即执行
await writer.return(); // 等待写入结束

/*
最后，同步的数据结构，也可以使用异步 Generator 函数。
由于没有异步操作，所以也就没有使用await关键字。
*/
async function* createAsyncIterable(syncIterable) {
	for (const elem of syncIterable) {
		yield elem;
	}
}
